;+
; NAME:
;   FSC_INPUTFIELD
;
; PURPOSE:
;
;   The purpose of this compound widget is to provide an alternative
;   to the CW_FIELD widget offered in the IDL distribution. What has
;   always bothered me about CW_FIELD is that the text widgets do not
;   look editable to the users on Windows platforms. This program
;   corrects that deficiency and adds some features that I think
;   would be helpful. For example, you can now assign an event handler
;   to the compound widget. The program is written entirely as an object.
;   A companion program, COYOTE_FIELD, has much the same functionality,
;   but is written as a traditional compound widget. The point of writing
;   the same program in two different ways is to give you the opportunity
;   to compare and contrast the two methods. I personally think there
;   is no substitute for the power of object programs. :-)
;
; AUTHOR:
;   FANNING SOFTWARE CONSULTING
;   David Fanning, Ph.D.
;   1645 Sheely Drive
;   Fort Collins, CO 80526 USA
;   Phone: 970-221-0438
;   E-mail: davidf@dfanning.com
;   Coyote's Guide to IDL Programming: http://www.dfanning.com/
;
; CATEGORY:
;
;   General programming.
;
; CALLING SEQUENCE:
;
;   objectRef = FSC_INPUTFIELD(parent, Title='X Size: ", Value=256, /IntegerValue)
;
; INPUT PARAMETERS:
;
;   parent -- The parent widget ID of the compound widget. Required.
;
; INPUT KEYWORDS:
;
;   Column -- Set this keyword to have the Label Widget above the Text Widget.
;   CR_Only -- Set this keyword if you only want Carriage Return events. Note that no
;              events are returned unless the EVENT_PRO or EVENT_FUNC keywords are also used.
;   Decimal -- Set this keyword to the number of digits to the right of the decimal
;              point in FLOATVALUE and DOUBLEVALUE numbers.
;   Digits -- Set this keyword to the number of digits permitted in INTERGERVALUE and LONGVALUE numbers.
;   DoubleValue -- Set this keyword if you want DOUBLE values returned.
;   Event_Func -- Set this keyword to the name of an Event Function. If this
;                keyword is undefined and the Event_Pro keyword is undefined,
;                all compound widget events are handled internally and not
;                passed on to the parent widget.
;   Event_Pro -- Set this keyword to the name of an Event Procedure. If this
;                keyword is undefined and the Event_Func keyword is undefined,
;                all compound widget events are handled internally and not
;                passed on to the parent widget.
;   FieldFont -- The font name for the text in the Text Widget.
;   FloatValue -- Set this keyword for FLOAT values.
;   Focus_Events -- Set this keyword if you only want text events when the keyboard focus is
;                moved out of the text widget. Note that no events are returned unless the
;                EVENT_PRO or EVENT_FUNC keywords are also used.
;   Frame -- Set this keyword to put a frame around the compound widget.
;   IntegerValue -- Set this keyword for INTEGER values.
;   LabelAlign -- Set this keyword to align label text. [0-center (default), 1-left, 2-right].
;   LabelFont -- The font name for the text in the Label Widget.
;   LabelSize -- The X screen size of the Label Widget.
;   LongValue -- Set this keyword for LONG values.
;   Name -- A scalar string name of the object. (default = '')
;   Positive -- Set this keyword if you want only positive numbers allowed.
;   Row=row -- Set this keyword to have the Label beside the Text Widget. (The default.)
;   Scr_XSize -- The X screen size of the compound widget.
;   Scr_YSize -- The Y screen size of the compound widget.
;   StringValue -- Set this keyword for STRING values. (The default.)
;   Title -- The text to go on the Label Widget.
;   UValue -- A user value for any purpose.
;   Value -- The "value" of the compound widget.
;   XSize -- The X size of the Text Widget.
;
;   In addition, any keyword defined for WIDGET_TEXT, but not defined here (e.g., SENSITIVE), is
;   passed along without inspection to the text widget. Use of "extra" keywords is discouraged.
;
; COMMON BLOCKS:
;
;   None.
;
; RESTRICTIONS:
;
;   None.
;
; EVENT STRUCTURE:
;
;   All events are handled internally unless either the Event_Pro or Event_Func
;   keywords are used to assign an event handler to the compound widget. By
;   default all events generated by the text widget are passed to the assigned
;   event handler. If you wish to receive only Carriage Return events, set the
;   CR_Only keyword.
;
;   event = { FSC_INPUTFIELD_EVENTS, $  ; The name of the event structure.
;             ID: 0L, $                 ; The ID of the compound widget's top-level base.
;             TOP: 0L, $                ; The widget ID of the top-level base of the hierarchy.
;             HANDLER: 0L, $            ; The event handler ID. Filled out by IDL.
;             ObjRef: Obj_New(), $      ; The "self" object reference. Provided so you can call methods.
;             Value: Ptr_New(), $       ; A pointer to the widget value.
;             Type:""                   ; A string indicating the type of data in the VALUE field.
;           }                           ; Values are "INT", "LONG", "FLOAT", "DOUBLE", or "STRING".
;
; GETTING and SETTING VALUES:
;
;   Almost all the properties of the widget can be obtained or set via
;   the object's GetProperty and SetProperty methods (described below).
;   But since traditional compound widgets have the ability to get and
;   set the value of the compound widget, this capability is implemented
;   as special methods.
;
;   To get the value of the field, do this: value = objectRef->Get_Value()
;   To set the value of the field, so this: objectRef->Set_Value, value, /IntegerValue
;
;   The proper keyword should be used to set the data type of the value. If a keyword
;   is not used, the data type is determined from the value itself.
;
; OBJECT PROCEDURE METHODS:
;
;   GetProperty -- This method allows various properties of the widget to be
;       returned via output keywords. The keywords that are available are:
;
;       CR_Only -- A flag, if set, means only report carriage return events.
;       DataType -- The data type of the field variable.
;       Decimal -- Set this keyword to the number of digits to the right of the decimal
;              point in FLOATVALUE and DOUBLEVALUE numbers.
;       Digits -- Set this keyword to the number of digits permitted in INTERGERVALUE and LONGVALUE numbers.
;       Event_Func -- The name of the event handler function.
;       Event_Pro -- The name of the event handler function.
;       Positive -- Indicates if the Positive number flag is set (1) or not (0).
;       UValue -- The user value assigned to the compound widget.
;       Value -- The "value" of the compound widget.
;     Name -- A scalar string name of the object.
;
;   Resize -- This method allows you to resize the compound widget's text field.
;        The value parameter is an X screen size for the entire widget. The text
;        widget is sized by using the value obtained from this value minus the
;        X screen size of the label widget.
;
;          objectRef->Resize, screen_xsize_value
;
;   Set_Value -- This method allows you to set the "value" of the field. It takes
;       one positional parameter, which is the value.
;
;          objectRef->Set_Value, 5
;
;       Keywords available are these to set the type of the data. If keywords
;       are not used, the data type is determined from the value.
;
;       DoubleValue -- Set this keyword if you want DOUBLE values returned.
;       FloatValue -- Set this keyword for FLOAT values.
;       IntegerValue --  Set this keyword for INTEGER values.
;       LongValue -- Set this keyword for LONG values.
;       StringValue -- Set this keyword for STRING values. (The default.)
;
;   SetProperty -- This method allows various properties of the widget to be
;       set via input keywords. The keywords that are available are:
;
;       CR_Only -- Set this keyword if you only want Carriage Return events.
;       Decimal -- Set this keyword to the number of digits to the right of the decimal
;              point in FLOATVALUE and DOUBLEVALUE numbers.
;       Digits -- Set this keyword to the number of digits permitted in INTERGERVALUE and LONGVALUE numbers.
;       DoubleValue -- Set this keyword if you want DOUBLE values returned.
;       Event_Func -- Set this keyword to the name of an Event Function.
;       Event_Pro -- Set this keyword to the name of an Event Procedure.
;       FloatValue -- Set this keyword for FLOAT values.
;       IntegerValue --  Set this keyword for INTEGER values.
;       LabelSize --  The X screen size of the Label Widget.
;       LongValue -- Set this keyword for LONG values.
;       Name -- A scalar string name of the object. (default = '')
;       Positive -- Set this keyword to indicate only positive numbers are allowed.
;       Scr_XSize -- The X screen size of the text widget.
;       Scr_YSize -- The Y screen size of the text widget.
;       Sensitive -- Set to 1 to make the widget sensitive, and to 0 to make it insensitive.
;       StringValue -- Set this keyword for STRING values. (The default.)
;       Title -- The text to go on the Label Widget.
;       UValue -- A user value for any purpose.
;       Value -- The "value" of the compound widget.
;       XSize -- The X size of the Text Widget.
;
;   SetTabNext -- This method allows you to specify which field to go to when a TAB character
;      is typed in the text widget. See the Example program below for an example of how to
;      use this method.
;
; OBJECT FUNCTIONS METHODS:
;
;      Get_Value -- Returns the "value" of the field. No parameters. Will be undefined
;          if a "number" field is blank. Should be checked before using:
;
;          IF N_Elements(objectRef->Get_Value()) NE 0 THEN Print, Value is: ', objectRef->Get_Value()
;
;      GetID -- Returns the widget identifier of the compound widget's top-level base.
;         (The first child of the parent widget.) No parameters.
;
;      GetLabelSize -- Returns the X screen size of the label widget. No parameters.
;
;      GetTextID -- Returns the widget identifier of the compound widget's text widget.
;         No parameters.
;
;      GetTextSize -- Returns the X screen size of the text widget. No parameters.
;
; PRIVATE OBJECT METHODS:
;
;   Although there is really no such thing as a "private" method in IDL's
;   object implementation, some methods are used internally and not meant to
;   be acessed publicly. Here are a few of those methods. I list them because
;   it may be these private methods are ones you wish to override in subclassed
;   objects.
;
;      MoveTab -- This method moves the focus to the widget identified in the "next" field,
;        which must be set with the SetTabNext method. No parameters. Called automatically
;        when a TAB character is typed in the text widget.
;
;      Text_Events -- The main event handler method for the compound widget. All
;        text widget events are processed here.
;
;      ReturnValue -- This function method accepts a string input value and converts
;        it to the type of data requested by the user.
;
;      Validate -- This function method examines all text input and removes unwanted
;        characters, depending upon the requested data type for the field. It makes it
;        impossible, for example, to type alphanumeric characters in an INTEGER field.
;
; EXAMPLE:
;
;   An example program is provided at the end of the FSC_INPUTFIELD code. To run it,
;   type these commands:
;
;      IDL> .Compile FSC_InputField
;      IDL> Example
;
; NOTES:
;
;   IDL 6.2 introduced new TAB behavior, which broke the previous TAB behavior. New TAB behavior
;   is now supported, but FOCUS_EVENTS *must* be set on the widget for the new TAB events to
;   behave properly. See the EXAMPLE program for examples.
;
; DEPENDENCIES:
;
;   Requires DBLTOSTR from the Coyote Library:
;     http://www.dfanning.com/programs/dbltostr.pro
;
; MODIFICATION HISTORY:
;
;   Written by: David W. Fanning, 23 NOV 1999.
;   Added DECIMAL and DIGITS keywords, 2 Jan 2000, DWF.
;   Changed the calling sequence to that of a function rather than an object
;      creation call. This is more familiar to users of compound widgets. 4 Jan 00. DWF.
;   Added GetID and Resize methods. 7 Jan 00. DWF.
;   Added the Positive keyword and functionality. 12 Jan 00. DWF
;   Modified (slightly) the behavior on deleting characters. 12 Jan 00. DWF.
;   If a number field is blank, the Get_Value method will now return an undefined variable.
;      Be sure you check this value before you use it for something! 17 Jan 00. DWF.
;   Fixed a small typo: "aveDecimal" to "haveDecimal". 10 March 2000. DWF.
;   Added the ability to tab between FSC_INPUTFIELD widgets with the SetTabNext,
;      MoveTab, and GetTextID methods. 31 July 2000. DWF.
;   Added NAME field property, a scalar string name for the object 2 AUG 2000 BT
;   Added ObjRef field to the FSC_FIELD event structure and added field selection
;      for the TAB events added 31 July. 7 AUG 2000. DWF
;   Added GetTextSize and GetLabelSize methods for obtaining the X screen
;      size of the text and label widgets, respectively. 30 Jan 2001. DWF.
;   Added FOCUS_EVENTS keyword and fixed a problem with the event structure.
;      Also added better error handling. 5 January 2003. DWF.
;   Fixed a small problem in which input values were cast to strings inadvertently. 9 January 2004. DWF.
;   Fixed a small problem with error messages and using EVENT_FUNC. 14 January 2004. DWF.
;   Fixed a problem when setting ROW keyword. 23 February 2004. DWF.
;   IDL 6.2 introduced new TAB behavior, which broke the previous TAB behavior. New TAB behavior
;      is now supported, but FOCUS_EVENTS *must* be set for the new TAB events to behave properly.
;      10 August 2005. DWF.
;   Modified to covert double precision values to strings properly. 30 November 2005. DWF.
;   Added POSITIVE keyword to SETPROPERTY and GETPROPERTY methods. 25 February 2006. DWF.
;   Set the DYNAMIC_RESIZE keyword on the label widget. 25 February 2006. DWF.
;   Added SENSITIVE keyword to SetProperty documentation. 10 November 2006. DWF.
;   Fixed a small problem in which doubles were not being initialized correctly due
;      to an inadvertant extra line of code. 3 July 2007. DWF.
;   Fixed a small problem with input validation when the input is of BYTE type. 1 Oct 2008. DWF.
;   Set the default fonts to be the current widget font, rather than the default widget font. 4 Oct 2008. DWF.
;-
;******************************************************************************************;
;  Copyright (c) 2008, by Fanning Software Consulting, Inc.                                ;
;  All rights reserved.                                                                    ;
;                                                                                          ;
;  Redistribution and use in source and binary forms, with or without                      ;
;  modification, are permitted provided that the following conditions are met:             ;
;                                                                                          ;
;      * Redistributions of source code must retain the above copyright                    ;
;        notice, this list of conditions and the following disclaimer.                     ;
;      * Redistributions in binary form must reproduce the above copyright                 ;
;        notice, this list of conditions and the following disclaimer in the               ;
;        documentation and/or other materials provided with the distribution.              ;
;      * Neither the name of Fanning Software Consulting, Inc. nor the names of its        ;
;        contributors may be used to endorse or promote products derived from this         ;
;        software without specific prior written permission.                               ;
;                                                                                          ;
;  THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY        ;
;  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES    ;
;  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT     ;
;  SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,             ;
;  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED    ;
;  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;         ;
;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND             ;
;  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT              ;
;  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS           ;
;  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                            ;
;******************************************************************************************;
FUNCTION FSC_InputField_WidgetFont, DEFAULT=default

   ; Build a small widget to determine the current 
   ; and default widget fonts.
   
   base = Widget_Base(MAP=0)
   button = Widget_Button(base, Value='TEST')
   
   ; Checking before realization gives default font.
   defaultFont = Widget_Info(button, /FONTNAME)
   
   ; Checking after realization gives current font.
   Widget_Control, base, /REALIZE
   currentFont = Widget_Info(button, /FONTNAME)
   
   ; Clean up.
   Widget_Control, base, /DESTROY

   IF Keyword_Set(default) THEN $
        RETURN, defaultFont ELSE $
        RETURN, currentFont
    
END ;-----------------------------------------------------------------------------------------------------------------------------




FUNCTION FSC_InputField_ERROR_MESSAGE, theMessage, Error=error, Informational=information, $
   Traceback=traceback, NoName=noname, Title=title, _Extra=extra

On_Error, 2

   ; Check for presence and type of message.

IF N_Elements(theMessage) EQ 0 THEN theMessage = !Error_State.Msg
s = Size(theMessage)
messageType = s[s[0]+1]
IF messageType NE 7 THEN BEGIN
   Message, "The message parameter must be a string.", _Extra=extra
ENDIF

   ; Get the call stack and the calling routine's name.

Help, Calls=callStack
IF Float(!Version.Release) GE 5.2 THEN $
   callingRoutine = (StrSplit(StrCompress(callStack[1])," ", /Extract))[0] ELSE $
   callingRoutine = (Str_Sep(StrCompress(callStack[1])," "))[0]

   ; Are widgets supported?

widgetsSupported = ((!D.Flags AND 65536L) NE 0)
IF widgetsSupported THEN BEGIN

      ; If this is an error produced with the MESSAGE command, it is a trapped
      ; error and will have the name "IDL_M_USER_ERR".

   IF !ERROR_STATE.NAME EQ "IDL_M_USER_ERR" THEN BEGIN

      IF N_Elements(title) EQ 0 THEN title = 'Trapped Error'

         ; If the message has the name of the calling routine in it,
         ; it should be stripped out. Can you find a colon in the string?

      colon = StrPos(theMessage, ":")
      IF colon NE -1 THEN BEGIN

            ; Extract the text up to the colon. Is this the same as
            ; the callingRoutine? If so, strip it.

         IF StrMid(theMessage, 0, colon) EQ callingRoutine THEN $
            theMessage = StrMid(theMessage, colon+1)

      ENDIF

         ; Add the calling routine's name, unless NONAME is set.

      IF Keyword_Set(noname) THEN BEGIN
         answer = Dialog_Message(theMessage, Title=title, _Extra=extra, $
            Error=error, Information=information)
      ENDIF ELSE BEGIN
         answer = Dialog_Message(StrUpCase(callingRoutine) + ": " + $
            theMessage, Title=title, _Extra=extra, $
            Error=error, Information=information)
      ENDELSE

   ENDIF ELSE BEGIN

         ; Otherwise, this is an IDL system error.

      IF N_Elements(title) EQ 0 THEN title = 'System Error'

      IF StrUpCase(callingRoutine) EQ "$MAIN$" THEN $
         answer = Dialog_Message(theMessage, _Extra=extra, Title=title, $
            Error=error, Information=information) ELSE $
      IF Keyword_Set(noname) THEN BEGIN
         answer = Dialog_Message(theMessage, _Extra=extra, Title=title, $
            Error=error, Information=information)
      ENDIF ELSE BEGIN
         answer = Dialog_Message(StrUpCase(callingRoutine) + "--> " + $
            theMessage, _Extra=extra, Title=title, $
            Error=error, Information=information)
      ENDELSE
   ENDELSE
ENDIF ELSE BEGIN
      Message, theMessage, /Continue, /NoPrint, /NoName, /NoPrefix, _Extra=extra
      Print, '%' + callingRoutine + ': ' + theMessage
      answer = 'OK'
ENDELSE

   ; Provide traceback information if requested.

IF Keyword_Set(traceback) THEN BEGIN
   Help, /Last_Message, Output=traceback
   Print,''
   Print, 'Traceback Report from ' + StrUpCase(callingRoutine) + ':'
   Print, ''
   FOR j=0,N_Elements(traceback)-1 DO Print, "     " + traceback[j]
ENDIF

RETURN, answer
END ;-----------------------------------------------------------------------------------------------------------------------------


PRO FSC_InputField::MoveTab
IF  NOT Widget_Info(self.tabnext, /Valid_ID) THEN RETURN
Widget_Control, self.tabnext, /Input_Focus
Widget_Control, self.tabnext, Get_Value=theText
theText = theText[0]
Widget_Control, self.tabnext, Set_Text_Select=[0,StrLen(theText)]
END ;-----------------------------------------------------------------------------------------------------------------------------


PRO FSC_InputField::SetTabNext, nextID
self.tabnext = nextID
END ;-----------------------------------------------------------------------------------------------------------------------------


FUNCTION FSC_InputField::GetTextID

; This method returns the ID of the text widget of the compound widget.

RETURN, self.textID
END ;-----------------------------------------------------------------------------------------------------------------------------


PRO FSC_InputField::Resize, newsize

; This method resizes the widget by making the text widget fit the new size.

l = Widget_Info(self.labelID, /Geometry)
Widget_Control, self.textID, Scr_XSize = (newsize - l.scr_xsize)
END ;-----------------------------------------------------------------------------------------------------------------------------


FUNCTION FSC_InputField::GetID

; This method returns the ID of the top-level base of the compound widget.

RETURN, self.tlb
END ;-----------------------------------------------------------------------------------------------------------------------------


FUNCTION FSC_InputField::GetLabelSize

; This method returns the X screen size of the label widget.

geom = Widget_Info(self.labelID, /Geometry)
RETURN, geom.scr_xsize
END ;-----------------------------------------------------------------------------------------------------------------------------



FUNCTION FSC_InputField::GetTextSize

; This method returns the X screen size of the text widget.

geom = Widget_Info(self.textID, /Geometry)
RETURN, geom.scr_xsize
END ;-----------------------------------------------------------------------------------------------------------------------------



FUNCTION FSC_InputField::Geometry

; This method returns the geometry of the compound widget.

RETURN, Widget_Info(self.tlb,/Geometry)
END ;-----------------------------------------------------------------------------------------------------------------------------


FUNCTION FSC_InputField::Get_Value

; This method returns the actual value of the compound widget.

RETURN, *self.theValue
END ;-----------------------------------------------------------------------------------------------------------------------------


PRO FSC_InputField::Set_Value, value, IntegerValue=integervalue, $
  FloatValue=floatvalue, LongValue=longvalue, DoubleValue=doublevalue, $
  StringValue=stringvalue

; This method sets the value of the compound widget.

   ; Error Handling.

Catch, theError
IF theError NE 0 THEN BEGIN
   ok = FSC_InputField_ERROR_MESSAGE(/Traceback)
   RETURN
ENDIF

IF Keyword_Set(stringvalue) THEN dataType = 'STRING'
IF Keyword_Set(integervalue) THEN dataType = 'INT'
IF Keyword_Set(longvalue) THEN dataType = 'LONG'
IF Keyword_Set(floatvalue) THEN dataType = 'FLOAT'
IF Keyword_Set(doublevalue) THEN dataType = 'DOUBLE'

IF N_Elements(dataType) EQ 0 THEN dataType = Size(value, /TName)
IF dataType EQ 'BYTE' THEN BEGIN
    value = 0 > FIX(value) < 255
    dataType = 'INT'
ENDIF

self.dataType = datatype

IF self.dataType EQ 'DOUBLE' THEN theText = DblToStr(value) ELSE theText = StrTrim(value, 2)
theText = self->Validate(theText)

   ; Load the value in the widget.

Widget_Control, self.textID, Set_Value=theText, Set_Text_Select=[StrLen(theText),0]
self.theText = theText

   ; Set the actual value of the compound widget.

*self.theValue = self->ReturnValue(theText)
END ;-----------------------------------------------------------------------------------------------------------------------------



FUNCTION FSC_InputField::Validate, value

; This function eliminates illegal characters from a string that represents
; a number. The return value is a properly formatted string that can be turned into
; an INT, LONG, FLOAT, or DOUBLE value. This is a "private" method.
;
; + 43B
; - 45B
; . 46B
; 0 - 9 48B -57B
; 'eEdD' [101B, 69B, 100B, 68B]

   ; Error Handling.

Catch, theError
IF theError NE 0 THEN BEGIN
   ok = FSC_InputField_ERROR_MESSAGE(/Traceback)
   RETURN, ""
ENDIF
   ; A null string should be returned at once.

IF N_Elements(value) EQ 0 THEN value = ""
value = value[0]
IF value EQ "" THEN RETURN, String(value)

   ; No leading or trailing blank characters to evaluate.

value = StrTrim(value, 2)

   ; A string value should be returned at once. Nothing to check.

IF StrUpCase(self.datatype) EQ 'STRING' THEN RETURN, String(value)

   ; Check integers and longs. A "-" or "+" in the first character is allowed. Otherwise,
   ; only number between 0 and 9, or 43B to 57B.

IF StrUpCase(self.datatype) EQ 'INT' OR StrUpCase(self.datatype) EQ 'LONG' THEN BEGIN

   returnValue = Ptr_New(/Allocate_Heap)
   asBytes = Byte(value)

   IF self.positive THEN BEGIN
      IF (asBytes[0] EQ 43B) OR $
         (asBytes[0] GE 48B AND asBytes[0] LE 57B) THEN *returnValue = [asBytes[0]]
   ENDIF ELSE BEGIN
      IF (asBytes[0] EQ 45B) OR (asBytes[0] EQ 43B) OR $
         (asBytes[0] GE 48B AND asBytes[0] LE 57B) THEN *returnValue = [asBytes[0]]
   ENDELSE
   length = StrLen(asBytes)
   IF length EQ 1 THEN BEGIN
      IF N_Elements(*returnValue) EQ 0 THEN  *returnValue = [32B] ELSE $
            *returnValue = [asBytes[0]]
   ENDIF ELSE BEGIN
      FOR j=1,length-1 DO BEGIN
         IF (asBytes[j] GE 48B AND asBytes[j] LE 57B) THEN BEGIN
            IF N_Elements(*returnValue) EQ 0 THEN  *returnValue = [asBytes[j]] ELSE $
               *returnValue = [*returnValue, asBytes[j]]
         ENDIF
      ENDFOR
  ENDELSE
  IF N_Elements(*returnValue) NE 0 THEN retValue = String(*returnValue) ELSE retValue = ""
  Ptr_Free, returnValue

      ; Check for digit restrictions.

  IF self.digits GT 0 THEN BEGIN
      retValue = StrTrim(retValue, 2)
      IF StrMid(retValue, 0, 1) EQ "-" THEN digits = self.digits + 1 ELSE digits = self.digits
      retValue = StrMid(retValue, 0, digits)
  ENDIF

  RETURN, retValue

ENDIF

   ; Check floating and double values. (+,-) in first character or after 'eEdD'.
   ; Only numbers, signs, decimal points, and 'eEdD' allowed.

IF StrUpCase(self.datatype) EQ 'FLOAT' OR StrUpCase(self.datatype) EQ 'DOUBLE' THEN BEGIN
   returnValue = Ptr_New(/Allocate_Heap)
   asBytes = Byte(value)
   IF self.positive THEN BEGIN
      IF (asBytes[0] EQ 43B) OR $
         (asBytes[0] GE 48B AND asBytes[0] LE 57B) OR $
         (asBytes[0] EQ 46B) THEN *returnValue = [asBytes[0]]
      IF (asBytes[0] EQ 46B) THEN haveDecimal = 1 ELSE haveDecimal = 0
   ENDIF ELSE BEGIN
      IF (asBytes[0] EQ 45B) OR (asBytes[0] EQ 43B) OR $
         (asBytes[0] GE 48B AND asBytes[0] LE 57B) OR $
         (asBytes[0] EQ 46B) THEN *returnValue = [asBytes[0]]
      IF (asBytes[0] EQ 46B) THEN haveDecimal = 1 ELSE haveDecimal = 0
   ENDELSE
   haveExponent = 0
   length = StrLen(asBytes)
   prevByte = asBytes[0]
   exponents = Byte('eEdD')
   IF length EQ 1 THEN BEGIN
      IF N_Elements(*returnValue) EQ 0 THEN  *returnValue = [32B] ELSE $
            *returnValue = [asBytes[0]]
   ENDIF ELSE BEGIN
      FOR j=1,length-1 DO BEGIN
         IF (asBytes[j] GE 48B AND asBytes[j] LE 57B) THEN BEGIN
            IF N_Elements(*returnValue) EQ 0 THEN  *returnValue = [asBytes[j]] ELSE $
               *returnValue = [*returnValue, asBytes[j]]
            prevByte = asBytes[j]
         ENDIF ELSE BEGIN

            ; What kind of thing is it?

            IF (asBytes[j] EQ 46B) THEN BEGIN ; A decimal point.
               IF haveDecimal EQ 0 THEN BEGIN
                  *returnValue = [*returnValue, asBytes[j]]
                  haveDecimal = 1
                  prevByte = asBytes[j]
               ENDIF
            ENDIF

            IF (asBytes[j] EQ 45B) OR (asBytes[j] EQ 43B) THEN BEGIN ; A + or - sign.
               index = Where(exponents EQ prevByte, count)
               IF count EQ 1 AND haveExponent THEN BEGIN
                  *returnValue = [*returnValue, asBytes[j]]
                  haveDecimal = 1
                  prevByte = asBytes[j]
               ENDIF
            ENDIF

            index = Where(exponents EQ asBytes[j], count)
            IF count EQ 1 AND haveExponent EQ 0 THEN BEGIN ; An exponent
               *returnValue = [*returnValue, asBytes[j]]
               haveExponent = 1
               prevByte = asBytes[j]
            ENDIF
         ENDELSE
      ENDFOR
   ENDELSE
      IF N_Elements(*returnValue) NE 0 THEN BEGIN

      retValue = String(*returnValue)
      retValue = StrTrim(retValue, 2)

               ; Check for decimal restrictions

      IF self.decimal GE 0 THEN BEGIN
         theDecimalPt = StrPos(retValue, '.')
         IF theDecimalPt NE -1 THEN retValue = StrMid(retValue, 0, theDecimalPt + self.decimal + 1)
      ENDIF

   ENDIF ELSE retValue = ""
   Ptr_Free, returnValue

      ; Is this a representable number?

   testValue = self->ReturnValue(retValue)
   IF String(testValue) NE 'NULLVALUE' THEN numCheck = Finite(testValue) ELSE numCheck = 1
   IF numCheck THEN BEGIN
      RETURN, retValue
   ENDIF ELSE BEGIN
      Message, 'The requested number is not representable.'
   ENDELSE
ENDIF

END ;-----------------------------------------------------------------------------------------------------------------------------



PRO FSC_InputField_Event__Define

; The FSC_InputField_Event Structure. Sent only if EVENT_PRO or EVENT_FUNC keywords
; have defined an event handler for the top-level base of the compound widget.

   event = { FSC_InputField_Event, $; The name of the event structure.
             ID: 0L, $              ; The ID of the compound widget's top-level base.
             TOP: 0L, $             ; The widget ID of the top-level base of the hierarchy.
             HANDLER: 0L, $         ; The event handler ID. Filled out by IDL.
             Value: Ptr_New(), $    ; A pointer to the widget value.
             Type:"", $             ; A string indicating the type of data in the VALUE field.
                                    ; Values are "INT", "LONG", "FLOAT", "DOUBLE", or "STRING".
             ObjRef:Obj_New()}      ; The "self" object.


END ;-----------------------------------------------------------------------------------------------------------------------------



FUNCTION FSC_InputField::ReturnValue, inputValue

; This method takes a string and turns it into a number,
; depending upon the current data type of the compound widget.
; This is a "private" method. For numbers, if the input value
; is a null string, then an undefined variable is returned.

   ; Error Handling.

ON_IOERROR, CatchIt

CASE self.datatype OF
   'INT': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $
      retValue = 'NULLVALUE' ELSE retValue = Fix(inputValue)
   'LONG': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $
      retValue = 'NULLVALUE' ELSE retValue = Long(inputValue)
   'FLOAT' : IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $
      retValue = 'NULLVALUE' ELSE retValue = Float(inputValue)
   'DOUBLE': IF inputValue EQ "" OR inputValue EQ "-" OR inputValue EQ "+" THEN $
      retValue = 'NULLVALUE' ELSE retValue = Double(inputValue)
   'STRING' : retValue = inputValue
ENDCASE

RETURN, retValue

CatchIt:
   retValue = 'NULLVALUE'
   RETURN, retValue
END ;-----------------------------------------------------------------------------------------------------------------------------



FUNCTION FSC_InputField::TextEvents, event

; The event handler method for the text widget of the compound widget.

   ; Error Handling.

Catch, theError
IF theError NE 0 THEN BEGIN
   Catch, /Cancel
   ok = FSC_InputField_ERROR_MESSAGE(/Traceback)
   RETURN, 0
ENDIF

   ; Get the previous text, the current cursor location in the text widget,
   ; and indicate this is not a Carriage Return event.

previousText = self.theText
textLocation = Widget_Info(event.id, /Text_Select)
cr_event = 0

   ; Is this a keyboard focus event? Then process it differently.

IF Tag_Names(event, /Structure_Name) EQ 'WIDGET_KBRD_FOCUS' THEN BEGIN

   ; New behavior for TAB MODE functionality in IDL 6.2.
   IF Float(!Version.Release) GT 6.1 THEN BEGIN

      IF event.enter EQ 1 THEN BEGIN

         Widget_Control, self.textID, Get_Value=theText
         theText = theText[0]
         Widget_Control, self.textID, Set_Text_Select=[0,StrLen(theText)]

         ; Send the keyboard focus events if there is an event handler to accept them.
         IF self.event_func NE "" THEN BEGIN
            thisEvent = {FSC_InputField_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self}
            RETURN, thisEvent
         ENDIF

         IF self.event_pro NE "" THEN BEGIN
            thisEvent = {FSC_InputField_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self}
            RETURN, thisEvent
         ENDIF

         ; Out of here.
         RETURN, 0

      ENDIF ELSE BEGIN

         Widget_Control, self.textID, Set_Text_Select=[0,0]

         ; Send the keyboard focus events if there is an event handler to accept them.
         IF self.event_func NE "" THEN BEGIN
            thisEvent = {FSC_InputField_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self}
            RETURN, thisEvent
         ENDIF

         IF self.event_pro NE "" THEN BEGIN
            thisEvent = {FSC_InputField_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self}
            RETURN, thisEvent
         ENDIF

         ; Out of here.
         RETURN, 0

      ENDELSE

   ENDIF ELSE BEGIN

      IF event.enter EQ 1 THEN RETURN,0 ; Don't bother. Only interested in losing keyboard focus.

          ; Get the current contents of text widget. Validate it.

       Widget_Control, self.textID, Get_Value=newText
       newText = newText[0]
       validText = self->Validate(newText)

          ; Load the valid text.

      self.theText = validText
      testValue  = self->ReturnValue(validText)
      IF String(testValue) EQ "NULLVALUE" THEN BEGIN
          Ptr_Free, self.theValue
          self.theValue = Ptr_New(/Allocate_Heap)
      ENDIF ELSE *self.theValue = testValue

         ; Send the keyboard focus events if there is an event handler to accept them.

      IF self.event_func NE "" THEN BEGIN
         thisEvent = {FSC_InputField_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self}
         RETURN, thisEvent
      ENDIF

      IF self.event_pro NE "" THEN BEGIN
         thisEvent = {FSC_InputField_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self}
         RETURN, thisEvent
      ENDIF

      RETURN, 0
   ENDELSE
ENDIF

   ; What kind of event is this?

possibleTypes = ['INSERT SINGLE CHARACTER', 'INSERT MULTIPLE CHARACTERS', 'DELETE TEXT', 'SELECT TEXT']
thisType = possibleTypes[event.type]

   ; Branch on event type.

CASE thisType OF

   'INSERT SINGLE CHARACTER': BEGIN

            ; If the character is a TAB see if there is something to do.

         IF event.ch EQ 9B THEN BEGIN
            self->MoveTab
            RETURN, 0
         ENDIF

            ; Get the current contents of text widget. Validate it.

         Widget_Control, self.textID, Get_Value=newText
         newText = newText[0]
         validText = self->Validate(newText)

            ; If it is valid, leave it alone. If not, go back to previous text.

         IF validText NE newText THEN BEGIN

            Widget_Control, self.textID, Set_Value=previousText, Set_Text_Select=[textLocation[0]-1,0]

         ENDIF ELSE BEGIN

            self.theText = validText
            testValue  = self->ReturnValue(validText)
            IF String(testValue) EQ "NULLVALUE" THEN BEGIN
               Ptr_Free, self.theValue
               self.theValue = Ptr_New(/Allocate_Heap)
            ENDIF ELSE *self.theValue = testValue

         ENDELSE

            ; Is this a Carriage Return event?

         IF event.ch EQ 10B then cr_event = 1
      ENDCASE

   'INSERT MULTIPLE CHARACTERS': BEGIN

            ; Same as above, but for all the characters you are inserting.

         Widget_Control, self.textID, Get_Value=newText
         newText = newText[0]
         validText = self->Validate(newText)
         IF validText NE newText THEN BEGIN
            Widget_Control, self.textID, Set_Value=previousText, Set_Text_Select=[textLocation[0]-1,0]
         ENDIF ELSE BEGIN
            self.theText = validText
            testValue  = self->ReturnValue(validText)
            IF String(testValue) EQ "NULLVALUE" THEN BEGIN
               Ptr_Free, self.theValue
               self.theValue = Ptr_New(/Allocate_Heap)
            ENDIF ELSE *self.theValue = testValue
         ENDELSE
      ENDCASE

   'DELETE TEXT': BEGIN

            ; Get the current contents of text widget. Validate it.

         Widget_Control, self.textID, Get_Value=newText
         newText = newText[0]
         validText = self->Validate(newText)

            ; Load the valid text.

        Widget_Control, self.textID, Set_Value=validText, Set_Text_Select=[textLocation[0],0]
        self.theText = validText
        testValue  = self->ReturnValue(validText)
        IF String(testValue) EQ "NULLVALUE" THEN BEGIN
            Ptr_Free, self.theValue
            self.theValue = Ptr_New(/Allocate_Heap)
        ENDIF ELSE *self.theValue = testValue

      ENDCASE

   'SELECT TEXT': ; Nothing to do.

ENDCASE

   ; Do you report all events, or only Carriage Return events?


CASE 1 OF
   self.cr_only: BEGIN
      IF self.event_func NE "" THEN BEGIN
         thisEvent = {FSC_InputField_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self}
         IF cr_event THEN RETURN, thisEvent
      ENDIF

      IF self.event_pro NE "" THEN BEGIN
         thisEvent = {FSC_InputField_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self}
         IF cr_event THEN RETURN, thisEvent
      ENDIF
      END
   self.focus:
   ELSE: BEGIN
      IF self.event_func NE "" THEN BEGIN
         thisEvent = {FSC_InputField_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self}
         RETURN, thisEvent
      ENDIF

      IF self.event_pro NE "" THEN BEGIN
         thisEvent = {FSC_InputField_Event, self.tlb, event.top, 0L, self.theValue, self.dataType, self}
         RETURN, thisEvent
      ENDIF
      END
ENDCASE



RETURN, 0
END ;-----------------------------------------------------------------------------------------------------------------------------



FUNCTION FSC_InputField_Event_Handler, event

; The main event handler for the compound widget. It reacts
; to "messages" in the UValue of the text widget.
; The message indicates which object method to call. A message
; consists of an object method and the self object reference.

Widget_Control, event.ID, Get_UValue=theMessage

event = Call_Method(theMessage.method, theMessage.object, event)

RETURN, event
END ;-----------------------------------------------------------------------------------------------------------------------------



PRO FSC_InputField::GetProperty, $
;
; This method allows you to obtain various properties of the compound widget via output keywords.
;
   CR_Only=cr_only, $               ; Get the value of the self.cr_only flag..
   DataType=datatype, $             ; The datatype of the compound widget.
   Decimal=decimal, $               ; Get the number of digits to the right of the decimal
                                    ;   point in FLOATVALUE and DOUBLEVALUE numbers.
   Digits=digits, $                 ; The number of digits permitted in INTERGERVALUE and LONGVALUE numbers.
   Event_Func=event_func, $         ; Get the name of an Event Function.
   Event_Pro=event_pro, $           ; Get the name of an Event Procedure.
   Focus_Events=focus_event, $      ; Get the current state of the self.focus flag.
   Name=Name, $                     ; Get the name of the object.
   Positive=positive, $             ; Get the positive state of the object.
   UValue=uvalue, $                 ; Get the user value of this object.
   Value=value                      ; Get the "value" of the compound widget.

   ; Error Handling.

Catch, theError
IF theError NE 0 THEN BEGIN
   ok = FSC_InputField_ERROR_MESSAGE(/Traceback)
   RETURN
ENDIF

   ; Get the properties.

cr_only = self.cr_only
datatype = self.datatype
decimal = self.decimal
digits = self.digits
event_func = self.event_func
event_pro = self.event_pro
positive = self.positive
uvalue = *self.uvalue
value = *self.thevalue
name = self.name
focus_event = self.focus

END ;--------------------------------------------------------------------------------------------------------------



PRO FSC_InputField::SetProperty, $
;
; This method allows you to set various properties of the compound widget.
;
   CR_Only=cr_only, $               ; Set this keyword if you only want Carriage Return events.
   Decimal=decimal, $               ; Set this keyword to the number of digits to the right of the decimal
                                    ;   point in FLOATVALUE and DOUBLEVALUE numbers.
   Digits=digits, $                 ; Set this keyword to the number of digits permitted in INTERGERVALUE and LONGVALUE numbers.
   DoubleValue=doublevalue, $       ; Set this keyword if you want DOUBLE values returned.
   Event_Func=event_func, $         ; Set this keyword to the name of an Event Function.
   Event_Pro=event_pro, $           ; Set this keyword to the name of an Event Procedure.
   FloatValue=floatvalue, $         ; Set this keyword for FLOAT values.
   Focus_Events=focus_events, $     ; Set this keyword to the current state of the self.focus flag.
   IntegerValue=integervalue, $     ; Set this keyword for INTEGER values.
   LabelSize=labelsize, $           ; The X screen size of the Label Widget.
   LongValue=longvalue, $           ; Set this keyword for LONG values.
   Name=name, $                     ; A scalar string name for the object.
   Positive=positive, $             ; The positive property.
   Scr_XSize=scr_xsize, $           ; The X screen size of the text widget.
   Scr_YSize=scr_ysize, $           ; The Y screen size of the text widget.
   Sensitive=sensitive, $           ; Set to 1 to make the widget sensitive, and to 0 to make it insensitive.
   StringValue=stringvalue, $       ; Set this keyword for STRING values. (The default.)
   Title=title, $                   ; The text to go on the Label Widget.
   UValue=uvalue, $                 ; A user value for any purpose.
   Value=value, $                   ; The "value" of the compound widget.
   XSize=xsize                      ; The X size of the Text Widget.

   ; Error Handling.

Catch, theError
IF theError NE 0 THEN BEGIN
   ok = FSC_InputField_ERROR_MESSAGE(/Traceback)
   RETURN
ENDIF

   ; Set the properties, if needed.

IF N_Elements(cr_only) NE 0 THEN self.cr_only = Keyword_Set(cr_only)
IF Keyword_Set(decimal)THEN self.decimal = decimal
IF Keyword_Set(digits)THEN self.digits = digits
IF Keyword_Set(doublevalue)THEN self.datatype = 'DOUBLE'
IF N_Elements(event_func) NE 0 THEN self.event_func = event_func
IF N_Elements(event_pro) NE 0 THEN self.event_pro = event_pro
IF Keyword_Set(floatvalue)THEN self.datatype = 'FLOAT'
IF N_Elements(focus_events) NE 0 THEN self.focus = Keyword_Set(focus_events)
IF Keyword_Set(integervalue)THEN self.datatype = 'INT'
IF N_Elements(labelsize) NE 0 THEN BEGIN
   Widget_Control, self.labelID, XSize=labelsize
ENDIF
IF Keyword_Set(longvalue)THEN self.datatype = 'LONG'
IF N_Elements(scr_xsize) NE 0 THEN BEGIN
   self.scr_xsize = scr_xsize
   Widget_Control, self.textID, Scr_XSize=scr_xsize
ENDIF
IF N_Elements(positive) NE 0 THEN self.positive = positive
IF N_Elements(scr_ysize) NE 0 THEN BEGIN
   self.scr_ysize = scr_ysize
   Widget_Control, self.textID, Scr_YSize=scr_ysize
ENDIF
IF N_Elements(sensitive) NE 0 THEN Widget_Control, self.textID, Sensitive=sensitive
IF Keyword_Set(stringvalue)THEN self.datatype = 'STRING'
IF N_Elements(title) NE 0 THEN Widget_Control, self.labelID, Set_Value=title
IF N_Elements(uvalue) NE 0 THEN *self.uvalue = uvalue
If N_elements(Name) NE 0 Then Self.Name = String(Name[0])
IF N_Elements(xsize) NE 0 THEN BEGIN
   self.xsize = xsize
   Widget_Control, self.textID, XSize=xsize
ENDIF

IF N_Elements(value) NE 0 THEN BEGIN
   self.theText = StrTrim(value, 2)
   *self.theValue = self->ReturnValue(self.theText)
   Widget_Control, self.textID, Set_Value=self.theText
ENDIF ELSE BEGIN
  Widget_Control, self.textID, Get_Value=theText
  theText = theText[0]
  *self.theValue = self->ReturnValue(theText)
ENDELSE

END ;--------------------------------------------------------------------------------------------------------------



FUNCTION FSC_InputField::INIT, $    ; The compound widget FSC_InputField INIT method..
   parent, $                        ; The parent widget. Required for all compound widgets.
   Column=column, $                 ; Set this keyword to have Label above Text Widget.
   CR_Only=cr_only, $               ; Set this keyword if you only want Carriage Return events.
   Decimal=decimal, $               ; Set this keyword to the number of digits to the right of the decimal
                                    ;   point in FLOATVALUE and DOUBLEVALUE numbers.
   Digits=digits, $                 ; Set this keyword to the number of digits permitted in INTERGERVALUE and LONGVALUE numbers.
   DoubleValue=doublevalue, $       ; Set this keyword if you want DOUBLE values returned.
   Event_Func=event_func, $         ; Set this keyword to the name of an Event Function.
   Event_Pro=event_pro, $           ; Set this keyword to the name of an Event Procedure.
   _Extra=extra, $                   ; Passes along extra keywords to the text widget.
   FieldFont=fieldfont, $           ; The font name for the text in the Text Widget.
   FloatValue=floatvalue, $         ; Set this keyword for FLOAT values.
   Focus_Events=focus_events, $     ; Set this keyword to enable event reporting when the widget loses keyboard focus.
   Frame=frame, $                   ; Set this keyword to put a frame around the compound widget.
   IntegerValue=integervalue, $     ; Set this keyword for INTEGER values.
   LabelAlign=labelalign, $         ; Set this keyword to align label text.
   LabelFont=labelfont, $           ; The font name for the text in the Label Widget.
   LabelSize=labelsize, $           ; The X screen size of the Label Widget.
   LongValue=longvalue, $           ; Set this keyword for LONG values.
   Name=name, $                     ; A scalar string name for the object.
   Positive=positive, $             ; Set this keyword to indicate only positive numbers allowed in the field.
   Row=row, $                       ; Set this keyword to have the Label beside the Text Widget. (The default.)
   Scr_XSize=scr_xsize, $           ; The X screen size of the text widget.
   Scr_YSize=scr_ysize, $           ; The Y screen size of the text widget.
   StringValue=stringvalue, $       ; Set this keyword for STRING values. (The default.)
   Title=title, $                   ; The text to go on the Label Widget.
   UValue=uvalue, $                 ; A user value for any purpose.
   Value=value, $                   ; The "value" of the compound widget.
   XSize=xsize                      ; The X size of the Text Widget.

   ; Error Handling.

Catch, theError
IF theError NE 0 THEN BEGIN
   ok = FSC_InputField_ERROR_MESSAGE(/Traceback)
   RETURN, 0
ENDIF

   ; A parent is required.

IF N_Elements(parent) EQ 0 THEN BEGIN
   Message, 'A PARENT argument is required. Returning...', /Informational
   RETURN, -1L
ENDIF

   ; Check keyword values.

IF N_Elements(column) EQ 0 THEN column = 0
IF N_Elements(digits) EQ 0 THEN digits = 0 ELSE digits = Fix(digits)
IF N_Elements(decimal) EQ 0 THEN decimal = -1 ELSE decimal = Fix(decimal)
IF N_Elements(event_func) EQ 0 THEN event_func = ""
IF N_Elements(event_pro) EQ 0 THEN event_pro = ""
IF N_Elements(fieldfont) EQ 0 THEN fieldfont = FSC_InputField_WidgetFont()
IF N_Elements(frame) EQ 0 THEN frame = 0
IF N_Elements(labelalign) EQ 0 THEN labelalign = 0
IF N_Elements(labelfont) EQ 0 THEN labelfont = FSC_InputField_WidgetFont()
IF N_Elements(labelsize) EQ 0 THEN labelsize = 0
;IF N_Elements(scr_xsize) EQ 0 THEN scr_xsize = 0
;IF N_Elements(scr_ysize) EQ 0 THEN scr_ysize = 0
IF N_Elements(title) EQ 0 THEN title = "Input Value: "
IF N_Elements(uvalue) EQ 0 THEN uvalue = ""
IF N_Elements(value) EQ 0 THEN value = ""
IF N_Elements(xsize) EQ 0 THEN xsize = 0
IF N_Elements(row) EQ 0 AND column EQ 0 THEN row = 1

   ; What data type are we looking for?

dataType = 'STRING'
IF Keyword_Set(stringvalue) THEN dataType = 'STRING'
IF Keyword_Set(integervalue) THEN dataType = 'INT'
IF Keyword_Set(longvalue) THEN dataType = 'LONG'
IF Keyword_Set(floatvalue) THEN dataType = 'FLOAT'
IF Keyword_Set(doublevalue) THEN dataType = 'DOUBLE'

   ; Populate the object.

self.cr_only = Keyword_Set(cr_only)
self.datatype = datatype
self.decimal = decimal
self.digits = digits
self.focus = Keyword_Set(focus_events)
self.parent = parent
self.event_pro = event_pro
self.event_func = event_func
self.positive = Keyword_Set(positive)
self.uvalue = Ptr_New(uvalue)
If N_Elements(name) NE 0 Then self.name = String(name[0])

   ; Validate the input value.

IF dataType EQ 'DOUBLE' THEN theText = DblToStr(value) ELSE theText = StrTrim(value, 2)
theText = self->Validate(theText)
self.theText = theText

   ; Create the widgets.

self.tlb = Widget_Base( parent, $  ; The top-level base of the compound widget.
   Frame=frame, $
   Row=row, $
   Column=Keyword_Set(column), $
   Base_Align_Center=1, $
   UValue=uvalue, $
   Event_Pro=event_pro, $
   Event_Func=event_func )

; New TAB MODE functionality in IDL 6.2.
IF Float(!Version.Release) GT 6.1 THEN Widget_Control, self.tlb, TAB_MODE=1

self.labelID = Widget_Label( self.tlb, Value=title, Font=labelfont, $ ; The Label Widget.
  Scr_XSize=labelsize, Scr_YSize=scr_ysize, /Dynamic_Resize, $
  Align_Center=(labelalign eq 0)?1:0, $
  Align_Left=(labelalign EQ 1)?1:0, $
  Align_Right=(labelalign EQ 2)?1:0)

self.textID = Widget_Text( self.tlb, $  ; The Text Widget.
   Value=theText, $
   XSize=xsize, $
   YSize=1, $
   Font=fieldfont, $
   All_Events=1, $
   _Extra=extra, $
   Scr_YSize=scr_ysize, $
   Kbrd_Focus_Events=self.focus, $
   Event_Func='FSC_InputField_Event_Handler', $
   UValue={Method:"TextEvents", Object:self}, $
   Kill_Notify='FSC_InputField_Kill_Notify', $
   Editable=1 )

   ; If screen sizes are set, adjust the text widget size.
   IF N_Elements(scr_xsize) NE 0 THEN BEGIN
      tlbg = Widget_Info(self.tlb, /Geometry)
      textg = Widget_Info(self.textID, /Geometry)
      labelg = Widget_info(self.labelID, /Geometry)
      plussize = textg.scr_xsize + labelg.scr_xsize
      stuff = (tlbg.xpad*2) + tlbg.space
      IF plussize GT scr_xsize THEN $
         Widget_Control, self.textID, Scr_XSize=textg.scr_xsize-(plussize-scr_xsize)-stuff ELSE $
         Widget_Control, self.textID, Scr_XSize=textg.scr_xsize+(scr_xsize - plussize)-stuff
   ENDIF

   ; Set the actual return value of the compound widget.

self.theValue = Ptr_New(self->ReturnValue(theText))

   ; If CR_ONLY or FOCUS_EVENTS are turned on and EVENT_PRO and EVENT_FUNC keywords are
   ; unspecified, issue a warning message to the command log.

IF self.cr_only AND (self.event_pro EQ "" AND self.event_func EQ "") THEN $
   Message, /Information, 'WARNING: There is no specified event handler for carriage return events for this widget. Events will be swallowed.'
IF self.focus AND (self.event_pro EQ "" AND self.event_func EQ "") THEN $
   Message, /Information, 'WARNING: There is no specified event handler for keyboard focus events for this widget. Events will be swallowed.'

RETURN, 1
END ;--------------------------------------------------------------------------------------------------------------



PRO FSC_InputField_Kill_Notify, textID

; This widget call-back procedure makes sure the self object is
; destroyed when the widget is destroyed.

Widget_Control, textID, Get_UValue=message
Obj_Destroy, message.object
END ;--------------------------------------------------------------------------------------------------------------



PRO FSC_InputField::CLEANUP

; This method makes sure there are not pointers left on the heap.

Ptr_Free, self.theValue
Ptr_Free, self.uvalue
END ;--------------------------------------------------------------------------------------------------------------



PRO FSC_InputField__Define

   objectClass = { FSC_INPUTFIELD, $        ; The object class name.
                   parent: 0L, $            ; The parent widget ID.
                   tlb: 0L, $               ; The top-level base of the compound widget.
                   labelID: 0L, $           ; The label widget ID.
                   textID: 0L, $            ; The text widget ID.
                   theText: "", $           ; The actual text in the text widget.
                   theValue: Ptr_New(), $   ; The actual "value" of the text in the text widget. :-)
                   event_func: "", $        ; The name of the specified event handler function.
                   event_pro: "", $         ; The name of the specified event handler procedrue
                   cr_only: 0L, $           ; A flag meaning send only carriage return events.
                   focus: 0L, $             ; A flag to indicate focus events should be returned.
                   tabnext: 0L, $           ; The identifier of a widget to receive the cursor focus if a TAB character is detected.
                   uvalue: Ptr_New(), $     ; The user value of the compound widget.
                   decimal: 0, $            ; The number of decimals points in FLOAT and DOUBLE numbers.
                   digits: 0, $             ; The number of digits in INT and LONG numbers.
                   positive: 0, $           ; A flag meaning only positive numbers allowed.
                   datatype: "" , $         ; The type of data to be returned from the text widget.
                   Name: "" $               ; A scalar string name for the object
                  }

  END ;--------------------------------------------------------------------------------------------------------------


FUNCTION FSC_INPUTFIELD, $          ; The compound widget FSC_InputField.
   parent, $                        ; The parent widget. Required for all compound widgets.
   Column=column, $                 ; Set this keyword to have Label above Text Widget.
   CR_Only=cr_only, $               ; Set this keyword if you only want Carriage Return events.
   Decimal=decimal, $               ; Set this keyword to the number of digits to the right of the decimal
                                    ;   point in FLOATVALUE and DOUBLEVALUE numbers.
   Digits=digits, $                 ; Set this keyword to the number of digits permitted in INTERGERVALUE and LONGVALUE numbers.
   DoubleValue=doublevalue, $       ; Set this keyword if you want DOUBLE values returned.
   Event_Func=event_func, $         ; Set this keyword to the name of an Event Function.
   Event_Pro=event_pro, $           ; Set this keyword to the name of an Event Procedure.
   _Extra=extra, $                  ; Passes along extra keywords to the text widget.
   FieldFont=fieldfont, $           ; The font name for the text in the Text Widget.
   FloatValue=floatvalue, $         ; Set this keyword for FLOAT values.
   Focus_Events=focus_events, $     ; Set this keyword to enable event reporting when the widget loses keyboard focus.
   Frame=frame, $                   ; Set this keyword to put a frame around the compound widget.
   IntegerValue=integervalue, $     ; Set this keyword for INTEGER values.
   LabelAlign=labelalign, $         ; Set this keyword to align label text.
   LabelFont=labelfont, $           ; The font name for the text in the Label Widget.
   LabelSize=labelsize, $           ; The X screen size of the Label Widget.
   LongValue=longvalue, $           ; Set this keyword for LONG values.
   Positive=positive, $             ; Set this keyword to indicate only positive numbers allowed in the field.
   Name=name, $                     ; The name of the object.
   Row=row, $                       ; Set this keyword to have the Label beside the Text Widget. (The default.)
   Scr_XSize=scr_xsize, $           ; The X screen size of the text widget.
   Scr_YSize=scr_ysize, $           ; The Y screen size of the text widget.
   StringValue=stringvalue, $       ; Set this keyword for STRING values. (The default.)
   Title=title, $                   ; The text to go on the Label Widget.
   UValue=uvalue, $                 ; A user value for any purpose.
   Value=value, $                   ; The "value" of the compound widget.
   XSize=xsize                      ; The X size of the Text Widget.

RETURN, Obj_New("FSC_INPUTFIELD", $
   parent, $                        ; The parent widget. Required for all compound widgets.
   Column=column, $                 ; Set this keyword to have Label above Text Widget.
   CR_Only=cr_only, $               ; Set this keyword if you only want Carriage Return events.
   Decimal=decimal, $               ; Set this keyword to the number of digits to the right of the decimal
                                    ;   point in FLOATVALUE and DOUBLEVALUE numbers.
   Digits=digits, $                 ; Set this keyword to the number of digits permitted in INTERGERVALUE and LONGVALUE numbers.
   DoubleValue=doublevalue, $       ; Set this keyword if you want DOUBLE values returned.
   Event_Func=event_func, $         ; Set this keyword to the name of an Event Function.
   Event_Pro=event_pro, $           ; Set this keyword to the name of an Event Procedure.
   _Extra=extra, $                   ; Passes along extra keywords to the text widget.
   FieldFont=fieldfont, $           ; The font name for the text in the Text Widget.
   FloatValue=floatvalue, $         ; Set this keyword for FLOAT values.
   Focus_Events=focus_events, $     ; Set this keyword to enable event reporting when the widget loses keyboard focus.
   Frame=frame, $                   ; Set this keyword to put a frame around the compound widget.
   IntegerValue=integervalue, $     ; Set this keyword for INTEGER values.
   LabelAlign=labelalign, $         ; Set this keyword to align label text.
   LabelFont=labelfont, $           ; The font name for the text in the Label Widget.
   LabelSize=labelsize, $           ; The X screen size of the Label Widget.
   LongValue=longvalue, $           ; Set this keyword for LONG values.
   Name=name, $                     ; The name of the object.
   Positive=positive, $             ; Set this keyword to indicate only positive numbers allowed in the field.
   Row=row, $                       ; Set this keyword to have the Label beside the Text Widget. (The default.)
   Scr_XSize=scr_xsize, $           ; The X screen size of the text widget.
   Scr_YSize=scr_ysize, $           ; The Y screen size of the text widget.
   StringValue=stringvalue, $       ; Set this keyword for STRING values. (The default.)
   Title=title, $                   ; The text to go on the Label Widget.
   UValue=uvalue, $                 ; A user value for any purpose.
   Value=value, $                   ; The "value" of the compound widget.
   XSize=xsize)                     ; The X size of the Text Widget.
END


PRO Example_Event, event

; An example event handler for FSC_Input_Field.

Widget_Control, event.top, Get_UValue=info
Widget_Control, event.id, Get_UValue=thisEvent

; Not interested in losing keyboard focus events.
theName = Tag_Names(event, /Structure_Name)
IF theName EQ 'WIDGET_KBRD_EVENT' THEN BEGIN
   IF event.type EQ 0 THEN RETURN
ENDIF

; What kind of event is this?
CASE thisEvent OF
   'Field 1 Event': BEGIN
      Print, ''
      IF N_Elements(*event.value) EQ 0 THEN Print, 'Field 1 Value is Undefined' ELSE $
         Print, 'Field 1 Value: ', *event.value
      END
   'Field 2 Event': BEGIN
      Print, ''
      IF N_Elements(*event.value) EQ 0 THEN Print, 'Field 2 Value is Undefined' ELSE $
         Print, 'Field 2 Value: ', *event.value
      END
   'Field 3 Event': BEGIN
      Print, ''
      IF N_Elements(*event.value) EQ 0 THEN Print, 'Field 3 Value is Undefined' ELSE $
         Print, 'Field 3 Value: ', *event.value
      END
   'Print It': BEGIN
      theValue =info.field3->Get_Value()
      Print, ''
      Print, 'Field 3 Value: ', theValue
      END
   'Set It': BEGIN
      info.field3->Set_Value, 'Coyote Rules!'
      END
   'Quit': Widget_Control, event.top, /Destroy
   'ChangeToFloat': BEGIN
      info.field1->SetProperty, Title='Float:', Value=RandomU(seed, 1)*100, /FloatValue
      END
   'ChangeToString': BEGIN
      info.field1->SetProperty, Title='String:', Value='Coyote Jules', /StringValue
      END
   'PrintFloat': BEGIN
      IF N_Elements(info.field2->Get_Value()) EQ 0 THEN Print, 'Field 2 Value is Undefined' ELSE $
         Print, 'Field 2 Value: ', info.field2->Get_Value()
      END
   'LabelSize': BEGIN
      Widget_Control, event.top, Update=0
      info.field1->SetProperty, LabelSize=75
      info.field2->SetProperty, LabelSize=75
      info.field3->SetProperty, LabelSize=75
      Widget_Control, event.top, Update=1
      END
ENDCASE
END ;----------------------------------------------------------------------------



PRO Example

; An example program to exercise some of the features of FSC_Input_FIELD.

tlb = Widget_Base(Column=1)
button = Widget_Button(tlb, Value='Change First Field to Float', $
   UValue='ChangeToFloat')
button = Widget_Button(tlb, Value='Change First Field to String', $
   UValue='ChangeToString')
field1 = FSC_INPUTFIELD(tlb, Title='Integer:', LabelSize=50, Digits=2, $
   Value=5, /IntegerValue, UValue='Field 1 Event', Event_Pro='Example_Event', /Focus_Events)
field2 = FSC_INPUTFIELD(tlb, Title='Float:', LabelSize=50, Value=45.6, /CR_Only, $
   /FloatValue, UValue='Field 2 Event', Event_Pro='Example_Event', Decimal=2, /Positive, /Focus_Events)
field3 = FSC_INPUTFIELD(tlb, Title='String:', LabelSize=50, Value='Coyote Rules!', $
   UValue='Field 3 Event', /Focus_Events, Event_Pro='Example_Event')

   ; Set up TABing between fields.

field1->SetTabNext, field2->GetTextID()
field2->SetTabNext, field3->GetTextID()
field3->SetTabNext, field1->GetTextID()

button = Widget_Button(tlb, Value='Print Value of String', UValue="Print It")
button = Widget_Button(tlb, Value='Set Value of String', UValue='Set It')
button = Widget_Button(tlb, Value='Change Size of Labels', UValue='LabelSize')
button = Widget_Button(tlb, Value='Print Floating Value', UValue='PrintFloat')
button = Widget_Button(tlb, Value='Quit', UValue='Quit')
Widget_Control, tlb, /Realize, Set_UValue={field1:field1, field2:field2, field3:field3}
XManager, 'example', tlb, /No_Block
END